home *** CD-ROM | disk | FTP | other *** search
/ BMUG Revelations / BMUG Revelations.toast / Utilities / Text and Speech / BBEdit 2.2.2 / BBEdit Extensions / Writing BBEdit Extensions < prev   
Text File  |  1993-01-04  |  16KB  |  395 lines

  1. Writing BBEdit Externals
  2.  
  3.  
  4. INTRODUCTION
  5.  
  6. BBEdit version 2.2 has a facility for calling code modules which are not
  7. part of the application itself. The main reason for this facility is so
  8. that users and third-party programmers can add specific functionality
  9. to BBEdit which goes beyond BBEdit's own charter. For example, one such
  10. code module might prepend Usenet attributions to each line in a selected
  11. range of text. This is a useful function, but it's not of interest to
  12. everyone.
  13.     
  14. GENERAL GUIDELINES
  15.  
  16. BBEdit code externals are built as standalone code resources. The 
  17. capability to build such resources is an integral part of THINK C
  18. and THINK Pascal. Users of MPW can also build standalone code
  19. resources, but with less ease.
  20.  
  21. BBEdit code externals are built as resources of type 'BBXT'. There
  22. may be any number of code externals per file, and code externals
  23. can use their own resources. Also, BBXT resources can be of any
  24. resource ID, since BBEdit manages the externals in such fashion
  25. that resource name or ID conflicts don't happen. Each BBXT resource
  26. in a file should have a name as assigned by ResEdit's "Get Info"
  27. command; this name will appear under the "Extensions" menu in BBEdit.
  28. (Note that if there are BBXT resources from different files with
  29. the same name, users may become confused. Neither I nor BBEdit 
  30. will arbitrate extension names.)
  31.  
  32. BBEdit extensions should be as friendly as possible. They should take
  33. great care to release any memory that they allocate while running, and
  34. they should leave no windows on the screen after they return to BBEdit.
  35. In general, BBEdit extensions should be considered one-shot text filters:
  36. they do their thing, then exit. They should put no menus in the menu
  37. bar, and should not have an event loop. (They *can* call ModalDialog. It's
  38. recommended that you use, or layer on top of, the standard filter that
  39. BBEdit provides.)
  40.  
  41. You should assume that any callback will move memory. This means that
  42. if you keep pointers into any relocatable blocks, pass addresses inside
  43. relocatable blocks as function arguments, you should lock the block
  44. first. For maximum friendliness, move it high with MoveHHi() first.
  45.  
  46. Extensions can put up modal dialogs and alerts, provided they're taken
  47. down again before the extension exits; they can also call Standard File
  48. or any system services necessary, as long as no attempt is made to
  49. bring another application to the front.
  50.  
  51. All other caveats with respect to managing A4 for code resources with
  52. globals remain in effect.
  53.  
  54. PROGRAMMING INTERFACE
  55.  
  56. Given all of these constraints, what *can* externals do?
  57.  
  58. The answer is: pretty much any transformation on a window's text that
  59. they please.
  60.  
  61. The interface to BBEdit is kept in a structure known as an
  62. "ExternalCallbackBlock". This structure begins with a 16-bit integer
  63. which is the version number of a callback block. If the callback
  64. block passed to you is higher than one you know about, then there
  65. is additional functionality available that you probably don't know
  66. about. Conversely, if the version number is less than the one you
  67. know about, some functionality that your extension requires may not
  68. be available.
  69.  
  70. The current callback interface version is 2.
  71.  
  72. Here is the C structure definition for an ExternalCallbackBlock:
  73.     
  74.     typedef struct {
  75.         short    version;
  76.         
  77.         //    version 1 callbacks
  78.         
  79.         pascal    Handle     (*GetWindowContents)(WindowPtr w);
  80.         pascal    void    (*GetSelection)(long *selStart, long *selEnd, long *firstChar);
  81.         pascal    void    (*SetSelection)(long selStart, long selEnd, long firstChar);
  82.         pascal    void    (*GetDocInfo)(WindowPtr w, Str255 fName, short *vRefNum, long *dirID);
  83.         pascal    long    (*GetModDate)(WindowPtr w);
  84.         pascal    Handle    (*Copy)(void);
  85.         pascal    Handle    (*Paste)(Handle pasteText);
  86.         
  87.         //    version 2 callbacks
  88.         
  89.         /*    Text-Editing stuff */
  90.         pascal    long        (*GetLastLine)(void);
  91.         pascal    long        (*GetLineNumber)(long selection);
  92.         pascal    long        (*GetLineStart)(long selection);
  93.         pascal    long        (*GetLineEnd)(long selection);
  94.         pascal    long        (*GetLinePos)(long line);
  95.         
  96.         pascal    void        (*Insert)(char *text, long len);
  97.         pascal    void        (*Delete)(void);
  98.         
  99.         /*    Getting and Setting window text */
  100.         pascal    void        (*SetWindowContents)(WindowPtr w, Handle h);
  101.         pascal    void        (*ContentsChanged)(WindowPtr w);
  102.         
  103.         /*    Reading file text */
  104.         pascal    Handle        (*GetFileText)(short vRefNum, long dirID, Str255 fName, Boolean *canDispose);
  105.     
  106.         /*    Direct user-interface calls */
  107.         pascal    Boolean        (*GetFolder)(Str255 prompt, short *vRefNum, long *dirID);
  108.         pascal    Boolean        (*OpenSeveral)(Boolean sort, short *file_count, StandardFileReply ***files);
  109.         
  110.         pascal    DialogPtr    (*CenterDialog)(short dialogID);
  111.         pascal    Boolean        (*StandardFilter)(DialogPtr d, EventRecord *event, short *item);
  112.         pascal    void        (*FrameDialogItem)(DialogPtr d, short item);
  113.         
  114.         pascal    WindowPtr    (*NewDocument)(void);
  115.         pascal    WindowPtr    (*OpenDocument)(void);
  116.     
  117.         /*    Utility Routines */
  118.         pascal    Handle        (*Allocate)(long size, Boolean clear);
  119.         pascal    long        (*FindPattern)(char *text, long text_len, long text_offset, 
  120.                                             char *pat, long pat_len,
  121.                                             Boolean case_sensitive);
  122.         
  123.         pascal    void        (*ReportOSError)(short code);
  124.         
  125.         /*    Preference routines */
  126.         pascal    void        (*GetPreference)(ResType prefType, short req_len, void *buffer, short *act_len);
  127.         pascal    void        (*SetPreference)(ResType prefType, short req_len, void *buffer, short *act_len);
  128.     } ExternalCallbackBlock;
  129.  
  130. Each field of the callback block is a pointer to a routine. Each routine
  131. is called with the Pascal calling convention; in the following descriptions
  132. the 'pascal' keyword is omitted for clarity.
  133.  
  134. Handle    (*GetWindowContents)(WindowPtr w);
  135.  
  136. returns a handle to the text in the window pointed to by "w". This
  137. routine should only be called on windows which have a windowKind
  138. of "userKind".
  139.  
  140. void    (*GetSelection)(long *selStart, long *selEnd, long *firstChar);
  141.  
  142. Sets the 32-bit integers pointed to by the arguments to the character
  143. offsets of the start of the selection, the end of the selection, and
  144. the first visible character in the active editing window.
  145.  
  146. void    (*SetSelection)(long selStart, long selEnd, long firstChar);
  147.  
  148. Sets the selection range and first visible character in the active
  149. editing window to the values passed. If "firstChar" is -1, the
  150. selection range will be centered in the window.
  151.  
  152. void    (*GetDocInfo)(WindowPtr w, Str255 *fName, short *vRefNum, short *dirID);
  153.  
  154. Returns information about the window pointed to by "w". If the window
  155. corresponds to a document that doesn't exist on disk, then fName will 
  156. be an empty string, and "vRefNum" and "dirID" will be set to zero.
  157. This routine should only be called on windows with a windowKind of
  158. "userKind".
  159.  
  160. long    (*GetModDate)(WindowPtr w);
  161.  
  162. Returns the modification date (in Macintosh time) of the document
  163. whose window is pointed to by "w". If the document is saved on disk,
  164. then the last-modified time of the file is returned; otherwise the
  165. time of last edit is returned.
  166. This routine should only be called on windows with a windowKind of
  167. "userKind".
  168.  
  169. Handle    (*Copy)(void);
  170.  
  171. Returns a handle to a copy of the text enclosed by the current
  172. selection in the active document. The CALLER is responsible
  173. for disposing of this handle when finished with it.
  174.  
  175. Handle    (*Paste)(Handle pasteText);
  176.  
  177. Pastes the text in the handle pointed to by "pasteText" into the
  178. current selection range of the active document. The CALLER is
  179. responsible for disposing of this handle when finished with it.
  180.  
  181. long        (*GetLastLine)(void);
  182.  
  183. Returns the number of lines in the active editing document.
  184.  
  185. long        (*GetLineNumber)(long selection);
  186.  
  187. Returns the line number of the character offset indicated by 'selection'.
  188.  
  189. long        (*GetLineStart)(long selection);
  190.  
  191. Returns the character offset of the beginning of the line that 'selection'
  192. is on.
  193.  
  194. long        (*GetLineEnd)(long selection);
  195.  
  196. Returns the character offset of the end of the line that 'selection'
  197. is on.
  198.  
  199. long        (*GetLinePos)(long line);
  200.  
  201. Returns the character offset of the beginning of 'line'.
  202.  
  203. void        (*Insert)(char *text, long len);
  204.  
  205. Inserts the 'len' characters pointed to by 'text' in the current selection
  206. range of the active editing document.
  207.  
  208. void        (*Delete)(void);
  209.  
  210. Deletes the characters enclosed by the selection range in the active
  211. editing document.
  212.  
  213. void        (*SetWindowContents)(WindowPtr w, Handle h);
  214.  
  215. Replaces the contents of the document designated by 'w' with the
  216. contents of the handle 'h'. •NOTE : after calling SetWindowContents,
  217. the handle belongs to the window, and MUST NOT BE DISPOSED. Also,
  218. if you modify the contents or size of the handle pointed to by "h"
  219. after using it in a SetWindowContents() call, be sure to call 
  220. ContentsChanged() for "w".
  221.  
  222. void        (*ContentsChanged)(WindowPtr w);
  223.  
  224. This routine should be called if you directly modify the text
  225. returned from a GetWindowContents() call.
  226.  
  227. Handle        (*GetFileText)(short vRefNum, long dirID, Str255 fName, Boolean *canDispose);
  228.  
  229. Loads the contents of the designated file's data fork into memory,
  230. and returns a handle to those contents. If there was an error
  231. (insufficient memory, file system error, etc), GetFileText()
  232. will return NIL.
  233.  
  234. The "canDispose" argument will be set to TRUE if the text was
  235. loaded from disk, FALSE if the text belongs to an open window.
  236. In the event that "canDispose" is TRUE, then you should dispose
  237. of the text (or use it in a SetWindowContents() call). If 
  238. "canDispose" is FALSE, then you MUST NOT DISPOSE THE HANDLE,
  239. or else you'll crash BBEdit. Also, you must not modify the
  240. contents of the handle if "canDispose" is FALSE.
  241.  
  242. Boolean        (*GetFolder)(Str255 prompt, short *vRefNum, long *dirID);
  243.  
  244. Displays a Standard File dialog box for choosing a folder. Returns
  245. TRUE if a folder was selected, FALSE if the user clicked the 
  246. "Cancel" button. The vRefNum and dirID of the chosen folder are
  247. returned in "vRefNum", and "dirID", respectively.
  248.  
  249. Boolean        (*OpenSeveral)(Boolean sort, short *file_count, StandardFileReply ***files);
  250.  
  251. Displays a Standard File box for choosing multiple files at once.
  252. Returns TRUE if the user chose any files, FALSE if the Cancel
  253. button was clicked. If "sort" is TRUE, then the files returned
  254. will be sorted in alphabetical order; otherwise, the files will
  255. be returned in the order the user added them to the list.
  256.  
  257. The number of files chosen will be returned in "file_count",
  258. and a handle to a list of StandardFileReply records (system 7 
  259. style) will be returned in "files".
  260.  
  261. DialogPtr    (*CenterDialog)(short dialogID);
  262.  
  263. Loads the dialog box indicated by "dialogID" and centers it on the
  264. screen. The dialog ID should correspond to a dialog which is 
  265. available in the external's resource file, and nowhere else.
  266.  
  267. Boolean        (*StandardFilter)(DialogPtr d, EventRecord *event, short *item);
  268.  
  269. This standard filter performs some useful standard behavior, such
  270. as outlining the default button with a thick border, and handling
  271. activates and deactivates for BBEdit's own windows. It is strongly
  272. recommended that you pass this pointer as the "filterProc" argument
  273. when calling ModalDialog() or Alert(). If you're writing custom
  274. dialog filters in your external, you should call this routine
  275. directly after doing your own preprocessing.
  276.  
  277. void        (*FrameDialogItem(DialogPtr d, short item);
  278.  
  279. This routine will draw a rectangle around the dialog item specified.
  280. If the item is a line, a line will be drawn using "true gray".
  281.  
  282. WindowPtr    (*NewDocument)(void);
  283.  
  284. Opens a new untitled document, and returns a pointer to its window.
  285. This document becomes the current document. Will return NIL if for
  286. some reason the window couldn't be opened.
  287.  
  288. WindowPtr    (*OpenDocument)(void);
  289.  
  290. Puts up BBEdit's standard Open dialog for choosing a file. If the
  291. user confirms the dialog and the document is successfully opened,
  292. returns a pointer to its window. Will return NIL if the user 
  293. cancels the dialog or if an error occurred while opening.
  294. (If some system error occurs, BBEdit will pose the alert box.)
  295.  
  296. Handle        (*Allocate)(long size, Boolean clear);
  297.  
  298. Allocates and returns a handle of "size" bytes. If the "clear"
  299. argument is TRUE, the handle will be zeroed. The handle returned
  300. will be a real handle, but may reside in MultiFinder temp memory.
  301. As with any handle, you should avoid locking handles returned by
  302. Allocate() for any length of time, and you should dispose of the
  303. handle before returning.
  304.  
  305. long        (*FindPattern)(char *text, long text_len, long text_offset, 
  306.                             char *pat, long pat_len,
  307.                             Boolean case_sensitive);
  308.  
  309. Searches the text buffer pointed to by "text" for the string of
  310. characters pointed to by "pat". "text_len" is the amount of text
  311. to search. "text_offset" is the position relative to the start
  312. of the text to start searching. "pat_len" is the length of the
  313. string to match. If "case_sensitive" is TRUE, then the case of
  314. potential matches will be checked.
  315.  
  316. FindPattern() will return the offset relative to the start of the
  317. text that the string was found. If the string was not found,
  318. FindPattern() will return -1.
  319.  
  320. void        (*ReportOSError)(short code);
  321.  
  322. Displays an alert box with the proper OS error message corresponding
  323. to the OS result code given in "code". This is handy for reporting
  324. filesystem errors, out of memory, and things of that sort.
  325.  
  326. void        (*GetPreference)(ResType prefType, short req_len, void *buffer, short *act_len);
  327. void        (*SetPreference)(ResType prefType, short req_len, void *buffer, short *act_len);
  328.  
  329. The GetPreference and SetPreference calls are for extensions to use
  330. to save and retrieve extension-specific information across runs.
  331. The settings are stored in the BBEdit Prefs file as resources.
  332.  
  333. GetPreference will retrieve the preference data stored in the
  334. resource of 'prefType', resource ID 128, and copy the contents
  335. of that resource into the data pointed to by "buffer". In all
  336. cases, "req_len" represents the maximum number of bytes which
  337. will be copied. (WARNING: the amount of data allocated in
  338. "buffer", be it a static structure or a handle, must be equal
  339. to or greater than "req_len", or else havoc will occur.)
  340. The word pointed to by "act_len" will be filled in with the
  341. actual number of bytes copied; this is always less than
  342. or equal to "req_len". If "act_len" is negative, the value
  343. in act_len is an OS error code (usually resNotFound if you're
  344. calling "GetPreference" with a virgin Preferences file).
  345.  
  346. SetPreference is the complement of GetPreference; it writes
  347. out the data in "buffer" to a resource of type "resType".
  348. "req_len" and "act_len" behave as for GetPreference.
  349.  
  350. The file containing 'main' for a BBEdit external looks something like this:
  351.  
  352. #include <SetupA4.h>
  353. #include "ExternalInterface.h"
  354.  
  355. pascal void main(ExternalCallbackBlock callbacks, WindowPtr w)
  356. {
  357.     RememberA0();
  358.     SetupA4();
  359.     
  360.     //    if the window is NIL, then don't do anything
  361.     
  362.     if (! w)
  363.         return;
  364.         
  365.     //    set the selection range to the start of the document, scrolled to the top
  366.     callbacks.SetSelection(0, 0, 0);
  367.     
  368.     //    insert some text at the beginning of the document
  369.     callbacks.Insert("hello world\r", 12);
  370.     
  371.     RestoreA4();
  372. }
  373.  
  374. A slightly more elaborate example is given in the "Prefix Lines" external.
  375.  
  376. OTHER DETAILS
  377.  
  378. Some things to take care of when building externals:
  379.  
  380. When BBEdit starts up, it takes account of the externals in the
  381. "BBEdit Extensions" folder. The "BBEdit Extensions" folder can
  382. reside in the same folder as BBEdit itself. Under System 6,
  383. the BBEdit Extensions folder can also reside in the system folder
  384. on the startup disk; under System 7, the BBEdit Extensions
  385. folder can also reside in the Extensions folder in the system
  386. folder on the startup disk.
  387.  
  388. Files containing externals must be of type 'BBXT'. The creator
  389. can be anything you like, although files with a creator of
  390. 'R*ch' will have an icon. (You can, of course, create bundle
  391. resources and icons to give the files any icons you desire.)
  392.  
  393. If there are no extensions available, no "Extensions" menu
  394. will be in the menu bar.
  395.